﻿#region Using namespaces

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using QuickGraph.Algorithms;
using BOL.Utils.Optimization.EvolutionaryAlgorithms;

#endregion

namespace BOL.Apps.GeneNetworkBrowser.Analysis.SubNetwork
{
    public class SubNetworkGeneticAlgorithm : GeneticAlgorithm<bool, Network>, IAnalysis
    {
        #region private variables

        private Network _network;
        private int _minNodes;
        private int _maxNodes;
        private int _timesNoChange;

        #endregion

        #region Constructor

        public SubNetworkGeneticAlgorithm(Random r, Network network, int minNodes, int maxNodes, int populationSize, int numberOfElites, SelectionMethod selectionMethod, double crossoverRate, double mutationRate, int numberOfIterations, bool allowRedundancyInPopulation, double tolerance, bool isVerbose, bool doParallel)
            : base(r, new SubNetworkGenome(r, network, GetConnTable(network), new bool[network.VertexCount], minNodes, maxNodes), populationSize, numberOfElites, selectionMethod, crossoverRate, mutationRate, numberOfIterations, allowRedundancyInPopulation, tolerance, isVerbose, doParallel)
        {
            this._name = String.Format("SubNetwork Genetic Algorithm: {0}", network.Name);

            this._network = network;
            this._minNodes = minNodes;
            this._maxNodes = maxNodes;
            this._timesNoChange = 0;

            if (isVerbose)
            {
                Console.WriteLine("   - network: {0}", _network);
                Console.WriteLine("   - size: [{0}, {1}]", _minNodes, _maxNodes);
            }

            this._k = 500;
        }

        private static Dictionary<int, List<int>> GetConnTable(Network network)
        {
            int numberOfVertices = network.VertexCount;
            int numberOfEdges = network.EdgeCount;
            Gene[] genes = network.Vertices.ToArray();
            Relation[] relations = network.Edges.ToArray();
            int source, target;
            var connTable = new Dictionary<int, List<int>>();

            foreach (var relation in relations)
            {
                source = Array.IndexOf(genes, relation.Source);
                target = Array.IndexOf(genes, relation.Target);

                if (connTable.ContainsKey(source))
                    connTable[source].Add(target);
                else
                {
                    var connGenes = new List<int>();
                    connGenes.Add(target);
                    connTable.Add(source, connGenes);
                }

                if (connTable.ContainsKey(target))
                    connTable[target].Add(source);
                else
                {
                    var connGenes = new List<int>();
                    connGenes.Add(source);
                    connTable.Add(target, connGenes);
                }
            }

            return connTable;
        }

        private static double[,] GetConnTable(Network network)
        {
            int numberOfVertices = network.VertexCount;
            int numberOfEdges = network.EdgeCount;
            Gene[] genes = network.Vertices.ToArray();
            Relation[] relations = network.Edges.ToArray();
            int source, target;
            var connTable = new double[numberOfVertices, numberOfVertices]

            foreach (var relation in relations)
            {
                source = Array.IndexOf(genes, relation.Source);
                target = Array.IndexOf(genes, relation.Target);

                if (connTable.ContainsKey(source))
                    connTable[source].Add(target);
                else
                {
                    var connGenes = new List<int>();
                    connGenes.Add(target);
                    connTable.Add(source, connGenes);
                }

                if (connTable.ContainsKey(target))
                    connTable[target].Add(source);
                else
                {
                    var connGenes = new List<int>();
                    connGenes.Add(source);
                    connTable.Add(target, connGenes);
                }
            }

            return connTable;
        }

        //private static IEnumerable<Tuple<int, int, IDictionary<int, int>>> GetCrossoverable(Network network)
        //{
        //    int numberOfVertices = network.VertexCount;
        //    int numberOfEdges = network.EdgeCount;
        //    Gene[] genes = network.Vertices.ToArray();
        //    Relation[] relations = network.Edges.ToArray();
        //    Relation relation;

        //    var crossoverable = new List<Tuple<int, int, IDictionary<int, int>>>();
        //    var components = new Dictionary<Gene, int>();
        //    int numberOfConnectedComponents = network.WeaklyConnectedComponents<Gene, Relation>(components);

        //    for (int j = 0; j < numberOfEdges; j++)
        //    {
        //        relation = relations[j];
        //        network.RemoveEdge(relation);

        //        if (network.WeaklyConnectedComponents<Gene, Relation>(components) > numberOfConnectedComponents)
        //        {
        //            var index = new Dictionary<int, int>();
        //            foreach (var component in components)
        //                index.Add(Array.IndexOf(genes, component.Key), component.Value);

        //            crossoverable.Add(new Tuple<int, int, IDictionary<int, int>>(Array.IndexOf(genes, relation.Source), Array.IndexOf(genes, relation.Target), index));
        //        }

        //        network.AddEdge(relation);
        //    }

        //    return crossoverable;
        //}

        //private static IEnumerable<Tuple<int, int, IDictionary<int, int>>> GetCrossoverable(Network network)
        //{
        //    int numberOfVertices = network.VertexCount;
        //    int numberOfEdges = network.EdgeCount;
        //    Gene[] genes = network.Vertices.ToArray();
        //    Relation[] relations = network.Edges.ToArray();
        //    Relation relation;

        //    var crossoverable = new List<Tuple<int, int, IDictionary<int, int>>>();
        //    var components = new Dictionary<Gene, int>();
        //    int numberOfConnectedComponents = network.WeaklyConnectedComponents<Gene, Relation>(components);

        //    for (int j = 0; j < numberOfEdges; j++)
        //    {
        //        relation = relations[j];
        //        network.RemoveEdge(relation);

        //        if (network.WeaklyConnectedComponents<Gene, Relation>(components) > numberOfConnectedComponents)
        //        {
        //            var index = new Dictionary<int, int>();
        //            foreach (var component in components)
        //                index.Add(Array.IndexOf(genes, component.Key), component.Value);

        //            if (index.Count(x => x.Value != 0) != 1)
        //                crossoverable.Add(new Tuple<int, int, IDictionary<int, int>>(Array.IndexOf(genes, relation.Source), Array.IndexOf(genes, relation.Target), index));
        //        }

        //        network.AddEdge(relation);
        //    }

        //    return crossoverable;
        //}

        //private static Dictionary<Relation, IDictionary<int, int>> GetCrossoverable(Network network)
        //{
        //    int numberOfVertices = network.VertexCount;
        //    int numberOfEdges = network.EdgeCount;
        //    Gene[] genes = network.Vertices.ToArray();
        //    Relation[] relations = network.Edges.ToArray();
        //    Relation relation;

        //    var crossoverable = new Dictionary<Relation, IDictionary<int, int>>();
        //    var components = new Dictionary<Gene, int>();
        //    int numberOfConnectedComponents = network.WeaklyConnectedComponents<Gene, Relation>(components);

        //    for (int j = 0; j < numberOfEdges; j++)
        //    {
        //        relation = relations[j];
        //        network.RemoveEdge(relation);

        protected override void InitializePopulation()
        {
            if (_isVerbose)
                Console.WriteLine("\n[Initialization]");

            // Initializes current population
            this._population = new List<IGenome<bool, Network>>();
            SubNetworkGenome offspring;
            var genes = _network.Vertices.ToArray();
            int numberOfGenes = _network.VertexCount;
            int i = 0;
            while (_population.Count < _populationSize)
            {
                if (i == numberOfGenes)
                    i = 0;

                offspring = (_seed as SubNetworkGenome).Offspring(genes[i++]);
                if (!_population.Contains(offspring))
                    _population.Add(offspring);
            }

            if (_isVerbose)
                Console.WriteLine(" : {0} genomes were successfully generated.", _populationSize);
        }

        protected override bool IsConverged(int i, double diff)
        {
            //if (diff < _tolerance)
            //    _timesNoChange++;
            //else
            //    _timesNoChange = 0;

            //if (_timesNoChange == 20 || i > _numberOfGenerations)
            //    return true;

            return i > _numberOfGenerations;
        }

        #endregion 

        #region IAnalysis implementation

        private string _name;

        public string Name { get { return _name; } set { _name = value; } }

        public IResult Analyze()
        {
            IEnumerable<Network> subNetworks = null;
            subNetworks = Optimize(_numberOfElites);

            return new SubNetworkResult(String.Format("Result: {0}", _name), subNetworks);
        }

        #endregion
    }
}
